//
//  UIView+Line.swift
//  ZJSwiftKit
//
//  Created by Fan Li Lin on 2023/3/15.
//  添加常用线、虚线类

import Foundation

/// 线条位置
public struct ZJLinePostion: OptionSet, @unchecked Sendable {
    public let rawValue: UInt
    
    public init(rawValue: UInt) {
        self.rawValue = rawValue
    }
    
    /// 顶部
    public static let top = ZJLinePostion(rawValue: 1 << 0)
    /// 水平中
    public static let centerX = ZJLinePostion(rawValue: 1 << 1)
    /// 底部
    public static let bottom = ZJLinePostion(rawValue: 1 << 2)
    /// 右边
    public static let right = ZJLinePostion(rawValue: 1 << 3)
    /// 垂直中
    public static let centerY = ZJLinePostion(rawValue: 1 << 4)
    /// 左边
    public static let left = ZJLinePostion(rawValue: 1 << 5)
    /// 四边
    public static let allEdge: ZJLinePostion = [.top, .left, .bottom, .right]
}

extension ZJPrefix where Base: UIView {
    
    /// 快速创建一个虚线
    /// - Parameters:
    ///   - color: 线条的颜色
    ///   - length: 单个线条的长度
    ///   - spacing: 线条之间的间距
    ///   - horizontal: 线条方向
    /// - Returns: UIView实例
    public static func dashLine(withColor color: UIColor, length: Float = 4.0, spacing: Float = 2.0, horizontal: Bool) -> UIView {
        let line = ZJLineDash()
        line.backgroundColor = .clear
        line.lineColor = color
        line.lineLength = length
        line.lineSpacing = spacing
        line.isHorizontal = horizontal
        return line
    }
    
    /// 快速创建一个小圆点虚线
    /// - Parameters:
    ///   - color: 小圆点的颜色
    ///   - spacing: 小圆点的之间的间距
    /// - Returns: UIView实例
    public static func dotLine(withColor color: UIColor, spacing: Float = 2.0) -> UIView {
        let line = ZJLineDot()
        line.backgroundColor = .clear
        line.lineColor = color
        line.lineSpacing = spacing
        return line
    }
    
    /// 添加一条普通的直线
    /// - Parameters:
    ///   - position: 线条的位置
    ///   - color: 线条的颜色
    ///   - width: 线条的宽度
    public func addLine(withPosition position: ZJLinePostion, color: UIColor, width: Float = Double.zj.pixelWidth) {
        
        if position.contains(.top) { base.innerAddLine(withPosition: .top, color: color, width: width) }
        
        if position.contains(.left) { base.innerAddLine(withPosition: .left, color: color, width: width) }
        
        if position.contains(.bottom) { base.innerAddLine(withPosition: .bottom, color: color, width: width) }
        
        if position.contains(.right) { base.innerAddLine(withPosition: .right, color: color, width: width) }
        
        if position.contains(.centerY) { base.innerAddLine(withPosition: .centerY, color: color, width: width) }
        
        if position.contains(.centerX) { base.innerAddLine(withPosition: .centerX, color: color, width: width) }
    }
    
    /// 添加一条虚线
    /// - Parameters:
    ///   - position: 线条的位置
    ///   - color: 线条的颜色
    ///   - width: 线条的宽度
    ///   - length: 单个线条的长度
    ///   - spacing: 线条之间的间距
    public func addDashLine(withPosition position: ZJLinePostion, color: UIColor, width: Float = 2.0, length: Float = 4.0, spacing: Float = 2.0) {
        
        if position.contains(.top) { base.innerAddDashLine(withPosition: .top, color: color, width: width, length: length, spacing: spacing) }
        
        if position.contains(.left) { base.innerAddDashLine(withPosition: .left, color: color, width: width, length: length, spacing: spacing) }
        
        if position.contains(.bottom) { base.innerAddDashLine(withPosition: .bottom, color: color, width: width, length: length, spacing: spacing) }
        
        if position.contains(.right) { base.innerAddDashLine(withPosition: .right, color: color, width: width, length: length, spacing: spacing) }
        
        if position.contains(.centerY) { base.innerAddDashLine(withPosition: .centerY, color: color, width: width, length: length, spacing: spacing) }
        
        if position.contains(.centerX) { base.innerAddDashLine(withPosition: .centerX, color: color, width: width, length: length, spacing: spacing) }
    }
    
    /// 添加一条小圆点虚线
    /// - Parameters:
    ///   - position: 线条的位置
    ///   - color: 小圆点的颜色
    ///   - width: 小圆点的直径
    ///   - spacing: 小圆点的之间的间距
    public func addDotLine(withPosition position: ZJLinePostion, color: UIColor, width: Float = 2.0, spacing: Float = 2.0) {
        
        if position.contains(.top) { base.innerAddDotLine(withPosition: .top, color: color, width: width, spacing: spacing) }
        
        if position.contains(.left) { base.innerAddDotLine(withPosition: .left, color: color, width: width, spacing: spacing) }
        
        if position.contains(.bottom) { base.innerAddDotLine(withPosition: .bottom, color: color, width: width, spacing: spacing) }
        
        if position.contains(.right) { base.innerAddDotLine(withPosition: .right, color: color, width: width, spacing: spacing) }
        
        if position.contains(.centerY) { base.innerAddDotLine(withPosition: .centerY, color: color, width: width, spacing: spacing) }
        
        if position.contains(.centerX) { base.innerAddDotLine(withPosition: .centerX, color: color, width: width, spacing: spacing) }
    }
}

fileprivate extension UIView {
    struct IvarsKey {
        static let kLineStore = UnsafeRawPointer(bitPattern: "kLineStore".hashValue)
    }
    
     var lineStore: [String : AnyObject] {
        get {
            var store = objc_getAssociatedObject(self, IvarsKey.kLineStore!) as? [String : AnyObject]
            if store == nil {
                store = [String : AnyObject]()
                self.lineStore = store!
            }
            return store!
        }
        set(newValue) {
            objc_setAssociatedObject(self, IvarsKey.kLineStore!, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
    func innerAddLine(withPosition position: ZJLinePostion, color: UIColor, width: Float) {
        let line = UIView()
        line.backgroundColor = color
        
        addLine(withLine: line, position: position, lineWidth: width)
        layoutLine(withLine: line, position: position, lineWidth: width)
    }
    
    func innerAddDashLine(withPosition position: ZJLinePostion, color: UIColor, width: Float, length: Float, spacing: Float) {
        let line = ZJLineDash()
        line.backgroundColor = .clear
        line.lineColor = color
        line.lineLength = length
        line.lineSpacing = spacing
        line.isHorizontal = (position.contains(.top) || position.contains(.bottom) || position.contains(.centerY)) ? true : false
        
        addLine(withLine: line, position: position, lineWidth: width)
        layoutLine(withLine: line, position: position, lineWidth: width)
    }
    
    func innerAddDotLine(withPosition position: ZJLinePostion, color: UIColor, width: Float = 2.0, spacing: Float = 2.0) {
        let line = ZJLineDot()
        line.backgroundColor = .clear
        line.lineColor = color
        line.lineSpacing = spacing
        
        addLine(withLine: line, position: position, lineWidth: width)
        layoutLine(withLine: line, position: position, lineWidth: width)
    }
    
    func removeLine(withPosition position: ZJLinePostion) {
        let key = "\(position)"
        guard let view = lineStore[key] else { return }
        view.removeFromSuperview()
        lineStore.removeValue(forKey: key)
    }
    
    func addLine(withLine line: UIView, position: ZJLinePostion, lineWidth: Float) {
        let key = "\(position)"
        if ((lineStore[key]) != nil) {
            removeLine(withPosition: position)
        }
        
        lineStore[key] = line
        addSubview(line)
    }
    
    func layoutLine(withLine line: UIView, position: ZJLinePostion, lineWidth: Float) {
        line.translatesAutoresizingMaskIntoConstraints = false
        
        switch position {
        case .top:
            line.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0).isActive = true
            line.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0).isActive = true
            line.topAnchor.constraint(equalTo: topAnchor, constant: 0).isActive = true
            line.heightAnchor.constraint(equalToConstant: CGFloat(lineWidth)).isActive = true
            
        case .left:
            line.topAnchor.constraint(equalTo: topAnchor, constant: 0).isActive = true
            line.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0).isActive = true
            line.widthAnchor.constraint(equalToConstant: CGFloat(lineWidth)).isActive = true
            line.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0).isActive = true
            
        case .bottom:
            line.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0).isActive = true
            line.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0).isActive = true
            line.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0).isActive = true
            line.heightAnchor.constraint(equalToConstant: CGFloat(lineWidth)).isActive = true
            
        case .right:
            line.topAnchor.constraint(equalTo: topAnchor, constant: 0).isActive = true
            line.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0).isActive = true
            line.widthAnchor.constraint(equalToConstant: CGFloat(lineWidth)).isActive = true
            line.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0).isActive = true
            
        case .centerY:
            line.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0).isActive = true
            line.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0).isActive = true
            line.centerYAnchor.constraint(equalTo: centerYAnchor, constant: 0).isActive = true
            line.heightAnchor.constraint(equalToConstant: CGFloat(lineWidth)).isActive = true
            
        case .centerX:
            line.topAnchor.constraint(equalTo: topAnchor, constant: 0).isActive = true
            line.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0).isActive = true
            line.widthAnchor.constraint(equalToConstant: CGFloat(lineWidth)).isActive = true
            line.centerXAnchor.constraint(equalTo: centerXAnchor, constant: 0).isActive = true
            
        default:
            break
        }
    }
}

/// 虚线
fileprivate class ZJLineDash: UIView {
    
    var lineColor = UIColor.lightGray
    var lineLength: Float = 4.0
    var lineSpacing: Float = 2.0
    var isHorizontal = false
    
    override class var layerClass: AnyClass {
        return CAShapeLayer.self
    }
    
    var shapeLayer: CAShapeLayer {
        return layer as! CAShapeLayer
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func draw(_ rect: CGRect) {
        super.draw(rect)
        
        /// 1. var
        let lineWidth = isHorizontal ? rect.height : rect.width
        
        /// 2. config shapeLayer
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.strokeColor = lineColor.cgColor
        shapeLayer.lineWidth = CGFloat(lineWidth)
        shapeLayer.lineJoin = .round
        shapeLayer.lineDashPattern = [NSNumber(value: lineLength), NSNumber(value: lineSpacing)]
        
        /// 3. draw  path
        let path = CGMutablePath()
        if isHorizontal {
            path.move(to: CGPointMake(0.0, CGFloat(lineWidth) / 2))
            path.addLine(to: CGPointMake(frame.width, CGFloat(lineWidth) / 2))
        } else {
            path.move(to: CGPointMake(CGFloat(lineWidth) / 2, 0.0))
            path.addLine(to: CGPointMake(CGFloat(lineWidth) / 2, frame.height))
        }
        
        shapeLayer.path = path
    }
}

/// 圆点
fileprivate class ZJLineDot: UIView {
    
    var lineColor = UIColor.lightGray
    var lineSpacing: Float = 2.0
    
    override func draw(_ rect: CGRect) {
        super.draw(rect)
        
        guard let context = UIGraphicsGetCurrentContext() else { return }
        context.setFillColor(lineColor.cgColor)
        
        var lineWidth = rect.width
        var isHorizontal = false
        if rect.width > rect.height {
            lineWidth = rect.height
            isHorizontal = true
        }
        
        let radius = CGFloat(lineWidth / 2.0)
        let skip = CGFloat(2.0 * radius + CGFloat(lineSpacing))
        if isHorizontal {
            let length = CGFloat(rect.width)
            for i in stride(from: radius, through: length, by: skip) {
                context.addArc(center: CGPointMake(i, radius), radius: radius, startAngle: 0, endAngle: 2 * Double.pi, clockwise: false)
                context.drawPath(using: .fill)
            }
        } else {
            let length = CGFloat(rect.height)
            for i in stride(from: radius, through: length, by: skip) {
                context.addArc(center: CGPointMake(radius, i), radius: radius, startAngle: 0, endAngle: 2 * Double.pi, clockwise: false)
                context.drawPath(using: .fill)
            }
        }
    }
}

